﻿Imports System.IO

Public Class KujuReader
    Dim mNodes As NodeArrayList

    Public Class NodeArrayList
        Inherits CollectionBase

        Public Function Add(ByVal n As Node) As Integer
            Return List.Add(n)
        End Function

        Public Sub Remove(ByVal n As Node)
            List.Remove(n)
        End Sub

        Default Public ReadOnly Property Item(ByVal Index As Integer) As Node
            Get
                Return List(Index)
            End Get
        End Property

        Default Public ReadOnly Property Item(ByVal Name As String) As Node
            Get
                Return SearchForName(Name)
            End Get
        End Property

        Public Function SearchForName(ByVal strName As String) As Node
            Return SearchForName(strName, 0)
        End Function

        Public Function SearchForName(ByVal strName As String, ByVal Index As Integer) As Node
            Dim i As Integer

            For i = Index To List.Count - 1
                If CType(List.Item(i), Node).Name.ToUpper = strName.ToUpper Then Return CType(List.Item(i), Node)
            Next
            Return Nothing
        End Function
    End Class

    Public Class Node
        Private mName As String
        Private mValue As String
        Private mParent As Node
        Private mIndex As Integer
        Private mNodes As NodeArrayList

        Public Sub New()
            mNodes = New NodeArrayList
        End Sub

        Public Sub New(ByVal Name As String)
            Me.New()
            mName = Name
        End Sub

        Public Sub New(ByVal Name As String, ByRef Parent As Node)
            Me.New(Name)
            mParent = Parent
        End Sub

        Public Property Index() As Integer
            Get
                Return mIndex
            End Get
            Set(ByVal value As Integer)
                mIndex = value
            End Set
        End Property

        Public Property Name() As String
            Get
                Return mName
            End Get
            Set(ByVal value As String)
                mName = value
            End Set
        End Property

        Public Property Value() As String
            Get
                Return mValue
            End Get
            Set(ByVal value As String)
                mValue = value
            End Set
        End Property

        Public ReadOnly Property Nodes() As NodeArrayList
            Get
                Return mNodes
            End Get
        End Property

        Public ReadOnly Property Parent() As Node
            Get
                Return mParent
            End Get
        End Property
    End Class

    Public Sub New(ByVal strFile As String)
        Me.New(strFile, "")
    End Sub

    Public Sub New(ByVal strFile As String, ByVal strReadToNode As String)
        Dim sr As New StreamReader(strFile)
        Dim Parent As Node = Nothing
        Dim strLine As String
        Dim intStart As Integer
        Dim intEnd As Integer
        Dim strName As String = ""
        Dim n As Node = Nothing
        Dim strValue As String = ""
        Dim bReadToNode As Boolean
        Dim bExitAfterThisRead As Boolean

        mNodes = New NodeArrayList
        bReadToNode = (strReadToNode.Length > 0)

        strLine = sr.ReadLine
        Do Until strLine Is Nothing Or strLine.IndexOf("(") > -1
            strLine = sr.ReadLine
        Loop

        Do Until strLine Is Nothing
            strLine = strLine.Replace("\" & Chr(34), "`")
            intStart = strLine.IndexOf("(")
            Do Until intStart = -1
                If Not InQuotes(strLine, intStart) Then Exit Do
                intStart = strLine.IndexOf("(", intStart + 1)
            Loop

            'Start new node.
            If intStart > -1 Then
                strName = strLine.Substring(0, intStart - 1).Trim
                If bReadToNode Then
                    If strName = strReadToNode Then bExitAfterThisRead = True
                End If
                n = New Node(strName, Parent)
                If Parent Is Nothing Then
                    mNodes.Add(n)
                Else
                    Parent.Nodes.Add(n)
                End If
                Parent = n
                strValue = ""
            Else
                intStart = 0
            End If

            intEnd = strLine.IndexOf(")", intStart)
            Do Until intEnd = -1
                If Not InQuotes(strLine, intEnd) Then Exit Do
                intEnd = strLine.IndexOf(")", intEnd + 1)
            Loop

            If intEnd > -1 Then
                If intEnd > 0 Then
                    strValue &= strLine.Substring(intStart + 1, intEnd - intStart - 1)
                End If
                n.Value = ParseValue(strValue)
                Parent = Parent.Parent
                If bExitAfterThisRead Then Exit Do
            Else
                If strLine.Length - intStart > 1 Then
                    strValue &= strLine.Substring(intStart + 1, strLine.Length - intStart - 1)
                End If
            End If

            strLine = sr.ReadLine
        Loop
        sr.Close()
    End Sub

    Private Function InQuotes(ByVal strText As String, ByVal Index As Integer) As Boolean
        Dim intQuote As Integer = strText.LastIndexOf(Chr(34), Index)
        Dim bInQuote As Boolean = False

        If intQuote = -1 Then Return False

        Do While intQuote > -1
            bInQuote = Not bInQuote
            intQuote = strText.LastIndexOf(Chr(34), intQuote - 1)
        Loop

        If Not bInQuote Then
            Return False
        Else
            Return (strText.IndexOf(Chr(34), Index) > -1)
        End If
    End Function

    Public ReadOnly Property Nodes() As NodeArrayList
        Get
            Return mNodes
        End Get
    End Property

    Private Function ParseValue(ByVal strValue As String) As String
        Dim intStartQuote As Integer
        Dim intEndQuote As Integer
        Dim returnValue As String = ""

        If strValue.Length = 0 Then Return ""

        intStartQuote = strValue.IndexOf(Chr(34))
        If intStartQuote = -1 Then Return strValue
        Do Until intStartQuote = -1
            intEndQuote = strValue.IndexOf(Chr(34), intStartQuote + 1)
            If intEndQuote = -1 Then Return strValue
            returnValue &= strValue.Substring(intStartQuote + 1, intEndQuote - intStartQuote - 1)
            intStartQuote = strValue.IndexOf(Chr(34), intEndQuote + 1)
        Loop

        returnValue = returnValue.Replace("\n", vbCrLf)
        returnValue = returnValue.Replace("`", Chr(34))

        Return returnValue
    End Function
End Class
